package com.cz.video.common.vod;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vod.model.v20170321.*;
import com.cz.video.common.exception.ServiceException;
import com.cz.video.common.exception.VodException;
import com.cz.video.common.helper.LoginHelper;
import com.cz.video.common.storage.hander.AliyunStsHandler;
import com.cz.video.common.utils.JsonUtils;
import com.cz.video.common.utils.StringUtils;
import com.cz.video.common.vod.dto.*;
import com.cz.video.common.vod.constants.VodConstants;

import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import lombok.extern.log4j.Log4j2;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;


/**
 * @ClassName : AliyunVodProperties
 * @Description : 阿里云vod
 * @Author : 陈智
 * @Date: 2023/7/8  15:12
 */
@Data
@Log4j2
public class AliyunVod implements VodProvider {

    /**
     * 所使用的云存储平台
     */
    private static String SERVICE = VodConstants.ALI_YUN_SERVICE;

    /* 存储平台 */
    private String platform;
    private String accessKey;
    private String secretKey;

    /**
     * 启用STS
     */
    private Boolean enableSts=false;

    /**
     * 区域ID,sts服务器和oss服务器的区域id是统一的
     */
    private String regionId ;

    /**
     * 回调地址
     */
    private String callbackURL;

    /**
     * 播放授权 超时时间
     */
    private long playAuthTimeOut;

    /**
     * 阿里云sts处理器
     */
    private AliyunStsHandler stsHandler;

    /**
     * 是否开启回调鉴权
     */
    private Boolean enableCallBackAuthentication=false;

    /**
     * 回调鉴权密钥
     */
    private String authKey;

    private DefaultAcsClient client;

    private Set<String> transcodePrefixSet;

    public AliyunVod() {
        transcodePrefixSet= CollUtil.newHashSet("mp4", "MP4", "flv", "FLV","m3u8","M3U8", "mp3", "MP3","WEBM");
    }

    public String getSERVICE() {
        return SERVICE;
    }

    /**
     * 单例模式运行，不需要每次使用完再销毁了
     * 这里将第三方库原先代码改为了双重检查锁定来确保线程安全
     */
    public DefaultAcsClient getClient() {
        if (client == null) {
            synchronized (AliyunVod.class) {
                if (client == null) {
                    DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKey, secretKey);
                    client=new DefaultAcsClient(profile);
                }
            }
        }
        return client;
    }


    @Override
    public String getService() {
        return SERVICE;
    }

    /**
     * @param  name 视频名称
     * 获取音/视频上传地址和凭证
     * @return VodUploadAuth 获取音/视频上传地址和凭证响应数据
     */
    public VodUploadAuth createUploadVideo(String name, String fileName) {
        CreateUploadVideoRequest request = new CreateUploadVideoRequest();
        request.setTitle(name);
        request.setFileName(fileName);
        //如果文件后缀为mov,就设置一个转码为mp4的模板
        if (!transcodePrefixSet.contains(StringUtils.substringAfterLast(fileName,"."))) {
            request.setTemplateGroupId("2a93086abfc6481dc329997a389c8c23");
        }

        AliyunUserData data = new AliyunUserData();

        //设置回调方式，默认为http
        if (StringUtils.isNotBlank(callbackURL))
            data.setMessageCallback(new AliyunUserData.MessageCallback("https",callbackURL));

        //用户自定义的扩展字段，用于回调时透传返回
        ObjectNode extend = JsonUtils.getObjectMapper().createObjectNode();
        extend.put("userId", LoginHelper.getUserId());
        data.setExtend(JsonUtils.toJsonString(extend));
        request.setUserData(JsonUtils.toJsonString(data));
        request.setTitle("filename");
        CreateUploadVideoResponse response = null;
        try {
            response = getClient().getAcsResponse(request);
        } catch (ClientException e) {
            throw new VodException("上传视频授权创建异常",e.getMessage());
        }
        //打印信息
        if (StringUtils.isNotEmpty(request.getTemplateGroupId()))
            log.info("用户{}申请了vid为{}的视频凭证使用{}转码模板，文件名：{}",LoginHelper.getUserId(),response.getVideoId(), request.getTemplateGroupId(), fileName);
        else  log.info("用户{}申请了vid为{}的视频凭证，文件名：{}",LoginHelper.getUserId(),response.getVideoId(), fileName);
        VodUploadAuth auth = new VodUploadAuth();
        auth.setVid(response.getVideoId());
        auth.setUploadAuth(response.getUploadAuth());
        auth.setUploadAddress(response.getUploadAddress());
        return auth;
    }

    /**
     * 刷新音/视频上传凭证
     * @param vid 视频ID
     * @return RefreshUploadVideoResponse 刷新音/视频上传凭证响应数据
     */
    public VodUploadAuth refreshUploadAuth(String vid){
        RefreshUploadVideoRequest request = new RefreshUploadVideoRequest();
        //音频或视频ID
        request.setVideoId(vid);
        try {
          RefreshUploadVideoResponse response = getClient().getAcsResponse(request);
          VodUploadAuth auth = new VodUploadAuth();
          auth.setVid(response.getVideoId());
          auth.setUploadAuth(response.getUploadAuth());
          auth.setUploadAddress(response.getUploadAddress());
          return auth;
        } catch (ClientException e) {
            throw new VodException("上传视频授权刷新异常",e.getMessage());
        }
    }

    /*获取播放凭证函数*/
    public VodPlayAuth getVideoPlayAuth(String vid) {
        if (StringUtils.isBlank(vid))
            return null;
      GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
      request.setVideoId(vid);
      request.setAuthInfoTimeout(playAuthTimeOut);
      try {
          GetVideoPlayAuthResponse response = getClient().getAcsResponse(request);
          VodPlayAuth auth = new VodPlayAuth();
          auth.setPlayAuth(response.getPlayAuth());
          auth.setDuration(response.getVideoMeta().getDuration());
          auth.setPlayAuthTimeOut(System.currentTimeMillis()+playAuthTimeOut*1000);
          auth.setVid(request.getVideoId());
          return auth;
      } catch (ClientException e) {
          log.error(e);
          //TODO:cz:现在是巨愚蠢的解决vod转码中申请播放凭证给提示信息的方式
          if (StringUtils.contains(e.getMessage(),"Transcoding")){
              throw new ServiceException("获取视频授权失败：视频正在转码处理中，请稍后再试");
          }
        throw new VodException("获取视频播放时出现错误，请联系管理员");
      }
    }



    @Override
    public boolean checkCallbackValid(HttpServletRequest request) {
        //如果不开启校验，就直接校验通过
        if(!enableCallBackAuthentication)
            return true;
        String timestamp = request.getHeader(VodConstants.X_VOD_TIMESTAMP);
        String signature = request.getHeader(VodConstants.X_VOD_SIGNATURE);
        //计算正确md5值
        String md5 = DigestUtil.md5Hex(callbackURL + "|" + timestamp + "|" + authKey);
        return md5.equals(signature);
    }

    @Override
    public VodMetaInfo getMetaInfoByVid(String videoId) {
        GetMezzanineInfoRequest mezzanineInfoRequest = new GetMezzanineInfoRequest();
        mezzanineInfoRequest.setVideoId(videoId);
        try {
            GetMezzanineInfoResponse mezzanineInfoResponse = getClient().getAcsResponse(mezzanineInfoRequest);
            GetMezzanineInfoResponse.Mezzanine mezzanine = mezzanineInfoResponse.getMezzanine();
            VodMetaInfo metaInfo = VodMetaInfo.builder().vid(mezzanine.getVideoId()).bitrate(mezzanine.getBitrate()).duration(mezzanine.getDuration())
                .height(mezzanine.getHeight()).size(mezzanine.getSize()).width(mezzanine.getWidth()).fileURL(mezzanine.getFileURL())
                .fps(mezzanine.getFps()).status(mezzanine.getStatus()).build();
            return metaInfo;
        } catch (ClientException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public VideoInfo getVideoInfoByVid(String videoId) {
        GetVideoInfoRequest videoInfoRequest = new GetVideoInfoRequest();
        videoInfoRequest.setVideoId(videoId);
        try {
            GetVideoInfoResponse response = getClient().getAcsResponse(videoInfoRequest);
            GetVideoInfoResponse.Video video = response.getVideo();
            VideoInfo videoInfo = new VideoInfo();
            //简单复制一下属性
            BeanUtil.copyProperties(video,videoInfo);
            return videoInfo;
        } catch (ClientException e) {
            log.error(e);
            throw new ServiceException("获取视频信息时出现错误，请联系管理员");
        }
    }

    @Override
    public List<VideoInfo> getVideoInfoList(List<String> vidList) {
        if(CollUtil.isEmpty(vidList))
            return null;
        GetVideoInfosRequest getVideoInfosRequest = new GetVideoInfosRequest();
        //这个videoIds的格式就是vid之间用逗号隔开
        getVideoInfosRequest.setVideoIds(StringUtils.join(vidList,","));
        List<VideoInfo> videoInfoList = new ArrayList<>();
        try {
            GetVideoInfosResponse response = getClient().getAcsResponse(getVideoInfosRequest);
            List<GetVideoInfosResponse.Video> videoList = response.getVideoList();
            if (CollUtil.isNotEmpty(videoList)){
                videoList.forEach(video -> {
                    videoInfoList.add(BeanUtil.copyProperties(video, VideoInfo.class));
                });
            }
        } catch (ClientException e) {
            log.error(StringUtils.format("获取视频信息列表时出现错误,  vidList:{}", vidList),e);
            throw new VodException("获取视频信息时出现错误，请联系管理员");
        }
        return videoInfoList;
    }

    /**
     * TODO:cz:重构 上传辅助文件
     * 获取辅助媒资(水印、字幕等)上传地址和凭证
     * @return CreateUploadAttachedMediaResponse 获取辅助媒资上传地址和凭证响应数据
     * @throws Exception
     */
    public  CreateUploadAttachedMediaResponse createUploadAttachedMedia() throws Exception {
        CreateUploadAttachedMediaRequest request = new CreateUploadAttachedMediaRequest();
        //设置业务类型
        request.setBusinessType("watermark");
        //设置文件扩展名
        request.setMediaExt("gif");
        //设置标题
        request.setTitle("this is a sample");

        //AliyunUserData，用户自定义设置参数，用户需要单独回调URL及数据透传时设置(非必须)
        JSONObject userData = new JSONObject();

        //UserData回调部分设置
        //消息回调设置，指定时以此为准，否则以全局设置的事件通知为准
        JSONObject messageCallback = new JSONObject();
        //设置回调地址
        messageCallback.put("CallbackURL", "http://192.168.0.0/16");
        //设置回调类型，默认为http
        messageCallback.put("CallbackType", "http");
//        userData.put("MessageCallback", messageCallback.toJSONString());

        JSONObject extend = new JSONObject();
        extend.put("MyId", "user-defined-id");
//        userData.put("Extend", extend.toJSONString());
//
//        request.setUserData(userData.toJSONString());

        return getClient().getAcsResponse(request);
    }


    @Override
    public void close() throws Exception {
        if (client != null) {
            client.shutdown();
            client = null;
        }
        if (stsHandler!=null){
            stsHandler.close();
        }
    }
}
